package com.z001.snack.common.module.support.codegenerator.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.z001.snack.common.common.domain.BaseResponse;
import com.z001.snack.common.common.domain.PageResult;
import com.z001.snack.common.common.util.PageUtil;
import com.z001.snack.common.common.util.SmartStringUtil;
import com.z001.snack.common.module.support.codegenerator.constant.CodeGeneratorConstant;
import com.z001.snack.common.module.support.codegenerator.dao.CodeGeneratorConfigDao;
import com.z001.snack.common.module.support.codegenerator.dao.CodeGeneratorDao;
import com.z001.snack.common.module.support.codegenerator.domain.entity.CodeGeneratorConfigEntity;
import com.z001.snack.common.module.support.codegenerator.domain.model.*;
import com.z001.snack.common.module.support.codegenerator.domain.req.CodeDownloadReq;
import com.z001.snack.common.module.support.codegenerator.domain.req.CodeGeneratorConfigForm;
import com.z001.snack.common.module.support.codegenerator.domain.req.CodeGeneratorPreviewForm;
import com.z001.snack.common.module.support.codegenerator.domain.req.TableQueryForm;
import com.z001.snack.common.module.support.codegenerator.domain.resp.TableColumnVO;
import com.z001.snack.common.module.support.codegenerator.domain.resp.TableConfigVO;
import com.z001.snack.common.module.support.codegenerator.domain.resp.TableVO;
import com.z001.snack.common.module.support.codegenerator.manager.CodeGeneratorManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 代码生成器 Service
 *
 * @Author 1024创新实验室-主任: 卓大
 * @Date 2022-06-30 22:15:38
 * @Wechat zhuoda1024
 * @Email lab1024@163.com
 * @Copyright 1024创新实验室 （ https://1024lab.net ）
 */
@Slf4j
@Service
public class CodeGeneratorService {

    @Autowired
    private CodeGeneratorDao codeGeneratorDao;

    @Autowired
    private CodeGeneratorConfigDao codeGeneratorConfigDao;

    @Autowired
    private CodeGeneratorTemplateService codeGeneratorTemplateService;

    @Resource
    private CodeGeneratorManager codeGeneratorManager;

    //Entity 父类字段 ==> 生成的时候 去除基础字段
    private final List<String> BASE_ENTITY_FIELDS = Arrays.asList("row_status", "create_user", "create_user_name",
            "create_time", "update_user", "update_user_name", "update_time");


    /**
     * 列信息
     *
     * @param tableName
     * @return
     */
    public List<TableColumnVO> getTableColumns(String tableName) {
        List<TableColumnVO> tableColumnVOList = codeGeneratorDao.selectTableColumn(tableName);
        //去除基础字段
        tableColumnVOList = tableColumnVOList.stream().filter(item -> {
            return !BASE_ENTITY_FIELDS.contains(item.getColumnName());
        }).collect(Collectors.toList());
        return tableColumnVOList;
    }


    /**
     * 查询数据库表数据
     *
     * @param tableQueryForm
     * @return
     */
    public PageResult<TableVO> queryTableList(TableQueryForm tableQueryForm) {
        Page<?> page = PageUtil.convert2PageQuery(tableQueryForm);
        List<TableVO> tableVOList = codeGeneratorDao.queryTableList(page, tableQueryForm);
        return PageUtil.convert2PageResult(page, tableVOList);
    }

    /**
     * 获取 表的 配置信息
     *
     * @param table
     * @return
     */
    public TableConfigVO getTableConfig(String table) {

        TableConfigVO config = new TableConfigVO();

        CodeGeneratorConfigEntity codeGeneratorConfigEntity = codeGeneratorConfigDao.selectById(table);
        if (codeGeneratorConfigEntity == null) {
            return config;
        }

        if (SmartStringUtil.isNotEmpty(codeGeneratorConfigEntity.getBasic())) {
            CodeBasic basic = JSON.parseObject(codeGeneratorConfigEntity.getBasic(), CodeBasic.class);
            config.setBasic(basic);
        }

        if (SmartStringUtil.isNotEmpty(codeGeneratorConfigEntity.getFields())) {
            List<CodeField> fields = JSONArray.parseArray(codeGeneratorConfigEntity.getFields(), CodeField.class);
            config.setFields(fields);
        }

        if (SmartStringUtil.isNotEmpty(codeGeneratorConfigEntity.getInsertAndUpdate())) {
            CodeInsertAndUpdate insertAndUpdate = JSON.parseObject(codeGeneratorConfigEntity.getInsertAndUpdate(), CodeInsertAndUpdate.class);
            config.setInsertAndUpdate(insertAndUpdate);
        }

        if (SmartStringUtil.isNotEmpty(codeGeneratorConfigEntity.getDeleteInfo())) {
            CodeDelete deleteInfo = JSON.parseObject(codeGeneratorConfigEntity.getDeleteInfo(), CodeDelete.class);
            config.setDeleteInfo(deleteInfo);
        }

        if (SmartStringUtil.isNotEmpty(codeGeneratorConfigEntity.getQueryFields())) {
            List<CodeQueryField> queryFields = JSONArray.parseArray(codeGeneratorConfigEntity.getQueryFields(), CodeQueryField.class);
            config.setQueryFields(queryFields);
        }

        if (SmartStringUtil.isNotEmpty(codeGeneratorConfigEntity.getTableFields())) {
            List<CodeTableField> tableFields = JSONArray.parseArray(codeGeneratorConfigEntity.getTableFields(), CodeTableField.class);
            config.setTableFields(tableFields);
        }

        return config;
    }

    /**
     * 更新配置
     *
     * @param form
     * @return
     */
    public synchronized BaseResponse<String> updateConfig(CodeGeneratorConfigForm form) {
        long existCount = codeGeneratorDao.countByTableName(form.getTableName());
        if (existCount == 0) {
            return BaseResponse.error("表不存在，请联系后端查看下数据库");
        }

        CodeGeneratorConfigEntity codeGeneratorConfigEntity = codeGeneratorConfigDao.selectById(form.getTableName());
        boolean updateFlag = true;
        if (codeGeneratorConfigEntity == null) {
            codeGeneratorConfigEntity = new CodeGeneratorConfigEntity();
            updateFlag = false;
        }

        // 校验假删，必须有 deleted_flag 字段
        List<TableColumnVO> tableColumns = getTableColumns(form.getTableName());
        if (null != form.getDeleteInfo() && form.getDeleteInfo().getIsSupportDelete() && !form.getDeleteInfo().getIsPhysicallyDeleted()) {
            Optional<TableColumnVO> any = tableColumns.stream().filter(e -> e.getColumnName().equals(CodeGeneratorConstant.DELETED_FLAG)).findAny();
            if (!any.isPresent()) {
                return BaseResponse.error("表结构中没有假删字段：" + CodeGeneratorConstant.DELETED_FLAG + ",请仔细排查");
            }
        }

        // 校验表必须有主键
        if (!tableColumns.stream().filter(e -> "PRI".equalsIgnoreCase(e.getColumnKey())).findAny().isPresent()) {
            return BaseResponse.error("表必须有主键，请联系后端查看下数据库表结构");
        }

        codeGeneratorConfigEntity.setTableName(form.getTableName());
        codeGeneratorConfigEntity.setBasic(JSON.toJSONString(form.getBasic()));
        codeGeneratorConfigEntity.setFields(JSONArray.toJSONString(form.getFields()));
        codeGeneratorConfigEntity.setInsertAndUpdate(JSON.toJSONString(form.getInsertAndUpdate()));
        codeGeneratorConfigEntity.setDeleteInfo(JSON.toJSONString(form.getDeleteInfo()));
        codeGeneratorConfigEntity.setQueryFields(JSONArray.toJSONString(form.getQueryFields()));
        codeGeneratorConfigEntity.setTableFields(JSONArray.toJSONString(form.getTableFields()));

        if (updateFlag) {
            codeGeneratorConfigDao.updateById(codeGeneratorConfigEntity);
        } else {
            codeGeneratorConfigDao.insert(codeGeneratorConfigEntity);
        }
        return BaseResponse.success();
    }

    /**
     * 预览
     *
     * @param form
     * @return
     */
    public BaseResponse<String> preview(CodeGeneratorPreviewForm form) {
        long existCount = codeGeneratorDao.countByTableName(form.getTableName());
        if (existCount == 0) {
            return BaseResponse.error("表不存在，请联系后端查看下数据库");
        }

        CodeGeneratorConfigEntity codeGeneratorConfigEntity = codeGeneratorConfigDao.selectById(form.getTableName());
        if (codeGeneratorConfigEntity == null) {
            return BaseResponse.error("配置信息不存在，请先进行配置");
        }

        List<TableColumnVO> columns = getTableColumns(form.getTableName());
        if (CollectionUtils.isEmpty(columns)) {
            return BaseResponse.error("表没有列信息无法生成");
        }

        String result = codeGeneratorTemplateService.generate(form.getTableName(), form.getTemplateFile(), codeGeneratorConfigEntity);
        return BaseResponse.success(result);

    }

    /**
     * 下载代码
     *
     * @param tableName
     * @return
     */
    public BaseResponse<byte[]> download(String tableName) {
        if (SmartStringUtil.isBlank(tableName)) {
            return BaseResponse.error("表名不能为空");
        }

        long existCount = codeGeneratorDao.countByTableName(tableName);
        if (existCount == 0) {
            return BaseResponse.error("表不存在，请联系后端查看下数据库");
        }

        CodeGeneratorConfigEntity codeGeneratorConfigEntity = codeGeneratorConfigDao.selectById(tableName);
        if (codeGeneratorConfigEntity == null) {
            return BaseResponse.error("配置信息不存在，请先进行配置");
        }

        List<TableColumnVO> columns = getTableColumns(tableName);
        if (CollectionUtils.isEmpty(columns)) {
            return BaseResponse.error("表没有列信息无法生成");
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        codeGeneratorTemplateService.zipGeneratedFiles(out, tableName, codeGeneratorConfigEntity);
        return BaseResponse.success(out.toByteArray());
    }


    //################################# 自动初始化 表基础代码 ###############################################

    /**
     * 初始化所有表的基础代码
     */
    public void initCodeGenerator() {
        TableQueryForm tableQueryForm = new TableQueryForm();
        //查询数据库中的所有表
        List<TableVO> tableVOList = codeGeneratorDao.queryTableList(tableQueryForm);

        //查询配置表全部
        List<CodeGeneratorConfigEntity> codeGeneratorConfigEntityList = codeGeneratorManager.list();
        //已经配置过的数据库 ==> 不在初始化配置,可以手动修改
        List<String> tableNameList = codeGeneratorConfigEntityList.stream().map(CodeGeneratorConfigEntity::getTableName).collect(Collectors.toList());

        //待初始化表 list
        List<CodeGeneratorConfigEntity> insertConfigEntityList = new ArrayList<>();

        //遍历所有表,对没有配置过的表 进行配置
        tableVOList.forEach(item -> {
            //未配置过
            if (!tableNameList.contains(item.getTableName())) {
                CodeGeneratorConfigEntity initConfigEntity = new CodeGeneratorConfigEntity();
                initConfigEntity.setTableName(item.getTableName());
                //查询该表的所有字段
                List<TableColumnVO> tableColumnVOList = getTableColumns(item.getTableName());
                //初始化 basic
                initCodeBasic(initConfigEntity, item);
                //初始化 Fields
                initCodeFields(initConfigEntity, tableColumnVOList);
                //初始化 insert_and_update
                initInsertAndUpdate(initConfigEntity,tableColumnVOList);
                //初始化 delete
                initDelete(initConfigEntity);
                //初始化 query
                initQuery(initConfigEntity);
                //初始化 tableField
                initTableField(initConfigEntity,tableColumnVOList);

                initConfigEntity.setCreateTime(LocalDateTime.now());
                initConfigEntity.setUpdateTime(LocalDateTime.now());
                insertConfigEntityList.add(initConfigEntity);
            }
        });
        codeGeneratorManager.saveBatch(insertConfigEntityList);
    }


    /**
     * 初始化 basic
     *
     * @param initConfigEntity
     * @param tableInfo
     */
    private void initCodeBasic(CodeGeneratorConfigEntity initConfigEntity, TableVO tableInfo) {
        CodeBasic basic = new CodeBasic();
        String moduleName = StrUtil.toCamelCase(tableInfo.getTableName().replace(InitCode.DB_PREFIX, "").toLowerCase());
        basic.setModuleName(StrUtil.upperFirst(moduleName));
        basic.setJavaPackageName(InitCode.JAVA_PACKAGE_NAME + moduleName);
        basic.setDescription(tableInfo.getTableComment());
        basic.setCopyright("1.0");
        basic.setBackendAuthor(InitCode.BACKEND_AUTHOR);
        basic.setFrontAuthor(InitCode.FRONT_AUTHOR);
        basic.setBackendDate(LocalDateTime.now());
        basic.setFrontDate(LocalDateTime.now());
        initConfigEntity.setBasic(JSONObject.toJSONString(basic));
    }

    /**
     * 初始化Fields
     *
     * @param initConfigEntity
     * @param tableColumnVOList
     */
    private void initCodeFields(CodeGeneratorConfigEntity initConfigEntity, List<TableColumnVO> tableColumnVOList) {
        List<CodeField> fieldList = new ArrayList<>();
        //表所有字段
        tableColumnVOList.forEach(item -> {
            CodeField field = new CodeField();
            //默认不自增
            field.setAutoIncreaseFlag(Boolean.FALSE);
            field.setColumnComment(item.getColumnComment());
            field.setColumnName(item.getColumnName());
            field.setFieldName(StrUtil.toCamelCase(item.getColumnName().toLowerCase()));
            field.setJavaType(InitCode.javaTypeMap.get(item.getDataType()));
            field.setJsType(InitCode.jsTypeMap.get(item.getDataType()));
            field.setLabel(item.getColumnComment());
            field.setPrimaryKeyFlag(StrUtil.isNotBlank(item.getColumnKey()) && "PRI".equals(item.getColumnKey()) ? true : false);
            fieldList.add(field);
        });
        initConfigEntity.setFields(JSONArray.toJSONString(fieldList));
    }


    /**
     * 初始化 insert_and_update
     * @param initConfigEntity
     * @param tableColumnVOList
     */
    private void initInsertAndUpdate(CodeGeneratorConfigEntity initConfigEntity, List<TableColumnVO> tableColumnVOList){
        CodeInsertAndUpdate codeInsertAndUpdate= new CodeInsertAndUpdate();
        codeInsertAndUpdate.setCountPerLine(InitCode.COUNT_PER_LINE);
        codeInsertAndUpdate.setIsSupportInsertAndUpdate(true);
        codeInsertAndUpdate.setPageType(InitCode.PAGE_TYPE);
        codeInsertAndUpdate.setWidth(InitCode.WIDTH);

        List<CodeInsertAndUpdateField> codeInsertAndUpdateFields = new ArrayList<>();
        tableColumnVOList.forEach(item->{
            CodeInsertAndUpdateField codeInsertAndUpdateField = new CodeInsertAndUpdateField();
            codeInsertAndUpdateField.setColumnName(item.getColumnName());
            codeInsertAndUpdateField.setRequiredFlag(false);
            codeInsertAndUpdateField.setInsertFlag(true);
            codeInsertAndUpdateField.setUpdateFlag(true);
            codeInsertAndUpdateField.setFrontComponent(InitCode.frontComponentMap.get(item.getDataType()));

            //数据库不能为空的 才选中必填
            if("NO".equals(item.getIsNullable())){
                //不能为空
                codeInsertAndUpdateField.setRequiredFlag(true);
            }
            //主键 新增的时候 不选择主键
            if(StrUtil.isNotBlank(item.getColumnKey()) && "PRI".equals(item.getColumnKey())){
                codeInsertAndUpdateField.setInsertFlag(false);
                codeInsertAndUpdateField.setUpdateFlag(true);
            }
            codeInsertAndUpdateFields.add(codeInsertAndUpdateField);
        });
        codeInsertAndUpdate.setFieldList(codeInsertAndUpdateFields);
        initConfigEntity.setInsertAndUpdate(JSONObject.toJSONString(codeInsertAndUpdate));
    }

    /**
     * 初始化 delete
     * @param initConfigEntity
     */
    private void initDelete(CodeGeneratorConfigEntity initConfigEntity){
        CodeDelete codeDelete = new CodeDelete();
        codeDelete.setDeleteEnum(InitCode.DELETE_ENUM);
        codeDelete.setIsSupportDelete(true);
        codeDelete.setIsPhysicallyDeleted(InitCode.IS_PHYSICALLY_DELETED);
        initConfigEntity.setDeleteInfo(JSONObject.toJSONString(codeDelete));
    }

    /**
     * 初始化 query
     * @param initConfigEntity
     */
    private void initQuery(CodeGeneratorConfigEntity initConfigEntity){
        //查询 默认为空
        initConfigEntity.setQueryFields(JSONArray.toJSONString(ArrayUtil.newArray(0)));
    }

    /**
     * 初始化 列表
     * @param initConfigEntity
     * @param tableColumnVOList
     */
    private void initTableField(CodeGeneratorConfigEntity initConfigEntity, List<TableColumnVO> tableColumnVOList){

        List<CodeTableField> codeTableFields = new ArrayList<>();
        tableColumnVOList.forEach(item->{
            CodeTableField codeTableField = new CodeTableField();
            codeTableField.setColumnName(item.getColumnName());
            codeTableField.setEllipsisFlag(true);
            codeTableField.setFieldName(StrUtil.toCamelCase(item.getColumnName().toLowerCase()));
            codeTableField.setLabel(item.getColumnComment());
            codeTableField.setShowFlag(true);
            codeTableFields.add(codeTableField);
        });
        initConfigEntity.setTableFields(JSONObject.toJSONString(codeTableFields));
    }



    public static void main(String[] args) {
        System.out.println( StrUtil.upperFirst(StrUtil.toCamelCase("heart_beat_record".toLowerCase())));
        System.out.println(StrUtil.toCamelCase("category_id".toLowerCase()));
        System.out.println(JSONArray.toJSONString(ArrayUtil.newArray(0)));
    }

    /**
     * 批量下载
     * @return
     */
    public BaseResponse<byte[]> downloadBatch(CodeDownloadReq req) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        if(CollUtil.isNotEmpty(req.getTableNameList())){
            codeGeneratorTemplateService.zipGeneratedFilesBatch(out, req.getTableNameList());
        }else{
            TableQueryForm tableQueryForm = new TableQueryForm();
            tableQueryForm.setPageNum(1);
            tableQueryForm.setPageSize(1000);
            PageResult<TableVO> result = queryTableList(tableQueryForm);
            List<TableVO> list = result.getList();
            List<String> tableNameList = list.stream().map(TableVO::getTableName).collect(Collectors.toList());
            codeGeneratorTemplateService.zipGeneratedFilesBatch(out, tableNameList);
        }
        return BaseResponse.success(out.toByteArray());
    }
}